package com.bckj.fastboot.core.operation.interceptor;

import com.bckj.fastboot.core.operation.context.OperationContext;
import com.bckj.fastboot.core.util.SpringUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Objects;
import java.util.Optional;

public class TxOperationInterceptor extends AbstractOperationInterceptor {

    private static final String TX_STATUS_KEY = "TX_STATUS";
    private static final String PROPAGATION_BEHAVIOR_KEY = "PROPAGATION_BEHAVIOR";

    private final PlatformTransactionManager transactionManager;

    public TxOperationInterceptor(OperationInterceptor interceptor) {
        super(interceptor);
        this.transactionManager = SpringUtils.getBean(PlatformTransactionManager.class);
    }

    @Override
    public void beforeExecute(OperationContext context) {
        super.beforeExecute(context);
        // 开启事务
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        int behavior = Optional.ofNullable(context.get(PROPAGATION_BEHAVIOR_KEY))
                .map(p -> Integer.parseInt(p.toString()))
                // 默认开启新事务
                .orElse(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);
        def.setPropagationBehavior(behavior);
        TransactionStatus status = transactionManager.getTransaction(def);
        context.put(TX_STATUS_KEY, status);
    }

    @Override
    public void afterExecute(OperationContext context, Throwable error) {
        super.afterExecute(context, error);
        Object status = context.get(TX_STATUS_KEY);
        Objects.requireNonNull(status, "TransactionStatus is null");
        if (error == null) {
            transactionManager.commit((TransactionStatus) status);
        } else {
            transactionManager.rollback((TransactionStatus) status);
        }
    }

    public static TxOperationInterceptor of() {
        return new TxOperationInterceptor(null);
    }

    public static TxOperationInterceptor of(OperationInterceptor interceptor) {
        return new TxOperationInterceptor(interceptor);
    }
}
